﻿using System;
using System.Linq;
using System.Text;
using biolit_config;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using Word = Microsoft.Office.Interop.Word;
using Microsoft.Office.Core;
using System.Diagnostics;
using System.Globalization;

namespace UCSD_BioLit
{
    /// <summary>
    /// Class used to load and store the Tag attributes information.
    /// </summary>
    public static class ControlsStorage
    {
        /// <summary>
        /// root node name for the xml document.
        /// </summary>
        private const string RootNodeName = "ControlsStore";

        /// <summary>
        /// control's node name.
        /// </summary>
        private const string ControlNodeName = "ControlNode";

        /// <summary>
        /// This is used to declare the custom namespace.
        /// </summary>
        private const string NamespaceDeclarationText = "urn:schemas-microsoft-com.";

        /// <summary>
        /// collection mapping the control ID and its controlProperties.
        /// </summary>
        private static Dictionary<Guid, ControlProperties> controlCollection = new Dictionary<Guid, ControlProperties>();

        /// <summary>
        /// Gets the control properties collection.
        /// </summary>
        public static Dictionary<Guid, ControlProperties> ControlCollection
        {
            get { return controlCollection; }
        }

        /// <summary>
        /// Method used to serialize the controls under a custom namespace.
        /// </summary>
        /// <param name="document">current document reference</param>
        /// <param name="controls">controlProperties list</param>
        /// <param name="controlsStoreNamespace">custom namespace</param>
        public static void Store(Word._Document document, ControlProperties[] controls, string controlsStoreNamespace)
        {
            string xml = null;

            if (controls != null && !string.IsNullOrEmpty(controlsStoreNamespace))
            {
                // declare the custom namespace.
                controlsStoreNamespace = NamespaceDeclarationText + controlsStoreNamespace;

                try
                {
                    // Serialize the controls under the root node and create the custom xml.
                    using (MemoryStream memStream = new MemoryStream())
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(ControlProperties[]));
                        serializer.WriteObject(memStream, controls);

                        byte[] buffer = new byte[memStream.Length];
                        buffer = memStream.GetBuffer();

                        XmlDocument doc = new XmlDocument();
                        XmlElement root = doc.CreateElement(RootNodeName, controlsStoreNamespace);
                        doc.AppendChild(root);

                        XmlElement controlsDataNode = doc.CreateElement(ControlNodeName, controlsStoreNamespace);
                        controlsDataNode.InnerXml = Encoding.ASCII.GetString(buffer);
                        root.AppendChild(controlsDataNode);

                        xml = doc.InnerXml;
                    }

                    // check for previous xmlparts stored under the custom namespace. 
                    // If present delete it.
                    CustomXMLParts parts = document.CustomXMLParts.SelectByNamespace(controlsStoreNamespace);
                    if (parts.Count > 0)
                    {
                        parts[1].Delete();
                    }

                    document.CustomXMLParts.Add(xml, Type.Missing);
                }
                catch (SerializationException ex)
                {
                    throw new Exception(Properties.Resources.STORE_ERROR_MESSAGE, ex);
                }
                catch (ArgumentOutOfRangeException ex)
                {
                    throw new Exception(Properties.Resources.STORE_ERROR_MESSAGE, ex);
                }
                catch (ArgumentException ex)
                {
                    throw new Exception(Properties.Resources.STORE_ERROR_MESSAGE, ex);
                }
            }
        }

        /// <summary>
        /// Method used to deserialize the custom xml part and retrieve the control properties collection.
        /// </summary>
        /// <param name="document">current active document</param>
        /// <param name="controlsStoreNamespace">custom namespace</param>
        /// <returns>controlproperties collection</returns>
        public static ControlProperties[] Load(Word._Document document, string controlsStoreNamespace)
        {
            ControlProperties[] controls = null;
            if (!string.IsNullOrEmpty(controlsStoreNamespace))
            {                
                // declare the custom namespace.
                controlsStoreNamespace = NamespaceDeclarationText + controlsStoreNamespace;

                // retrieve the xmlparts present under the custom namespace and  get the first most one.
                CustomXMLParts parts = document.CustomXMLParts.SelectByNamespace(controlsStoreNamespace);

                if (parts != null && parts.Count > 0)
                {
                    try
                    {
                        CustomXMLPart part = parts[1];
                        XmlDocument doc = new XmlDocument();
                        doc.LoadXml(part.XML);

                        XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
                        nsmgr.AddNamespace("sc", controlsStoreNamespace);

                        XmlElement controlsElement =
                            doc.SelectSingleNode(
                                                String.Format(CultureInfo.CurrentUICulture, "//sc:{0}", ControlNodeName),
                                                 nsmgr) as XmlElement;
                        if (controlsElement != null)
                        {
                            byte[] data = Encoding.ASCII.GetBytes(controlsElement.InnerXml);
                            DataContractSerializer deserializer = new DataContractSerializer(typeof(ControlProperties[]));
                            controls = (ControlProperties[])deserializer.ReadObject(new MemoryStream(data));
                        }
                    }
                    catch (XmlException ex)
                    {
                        throw new Exception(Properties.Resources.LOAD_ERROR_MESSAGE, ex);
                    }
                    catch (ArgumentException ex)
                    {
                        throw new Exception(Properties.Resources.LOAD_ERROR_MESSAGE, ex);
                    }
                    catch (SerializationException ex)
                    {
                        throw new Exception(Properties.Resources.LOAD_ERROR_MESSAGE, ex);
                    }
                }
            }

            return controls;
        }
    }
}
